#pragma once

#include "Socket.hpp"

#include <memory>
#include <unistd.h>
#include <sys/epoll.h>
#include <iostream>

using namespace SocketModule;
using namespace logModule;

class EpollServer
{
    static const int size = 64;
    static const int defaultfd = -1;

public:

    EpollServer(int port)
        :_listenSockfd(std::make_unique<TcpSocket>())
        ,_isRunning(false)
        ,_epfd(defaultfd)
    {
        _listenSockfd->buildTcpSocketMethod(port);

        _epfd = epoll_create(128);
        if (_epfd < 0)
        {
            LOG(LogLevel::FATAL) << "epoll_create failure";
            exit(1);
        }
        LOG(LogLevel::DEBUG) << "epoll_create success";

        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = _listenSockfd->getFd();

        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, _listenSockfd->getFd(), &ev);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "epoll_ctl failure";
            exit(1);
        }
        LOG(LogLevel::DEBUG) << "epoll_ctl success";
    }

    void start()
    {
        int timeout = -1;
        _isRunning = true;
        while (_isRunning)
        {
            // listenSockfd 自己的 IO 也应该放入 Poll ，让 OS 去通知 listenSockfd 的读事件
            int n = ::epoll_wait(_epfd, _revs, size, timeout);
            switch (n)
            {
            case -1:
                LOG(LogLevel::ERROR) << "epoll error";
                break;
            case 0:
                LOG(LogLevel::INFO) << "time out...";
                break;
            default:
                LOG(LogLevel::DEBUG) << "有事件就绪了... 数量:" << n;
                dispatcher(n);        // 事件派发
                break;
            }
        }
    }

    void dispatcher(int num)                    // 事件派发，分为 读新链接 和 读普通事件
    {
        for (int i = 0; i < num; ++i)
        {
            int sockfd = _revs[i].data.fd;
            int revent = _revs[i].events;

            if (revent & EPOLLIN)
            {
                if (sockfd == _listenSockfd->getFd())
                {
                    accepter();      // 处理新链接
                }
                else
                {
                    recver(sockfd);       // 普通的读事件
                }
            }

            // if (revent & EPOLLOUT)
            // {

            // }
        }
    }

    void accepter()
    {
        InetAddr client;
        int sockfd = _listenSockfd->acceptFd(client);   // 这里 accept 不会阻塞
        if (sockfd < 0)
        {
            return;
        }

        LOG(LogLevel::DEBUG) << "get new link, sockfd:" << sockfd << " client:" << client.getStringAddr();
        
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = sockfd;

        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ev);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "epoll_ctl failure";
            exit(1);
        }
        LOG(LogLevel::DEBUG) << "epoll_ctl success add new sockfd: " << sockfd;
    }

    void recver(int sockfd)
    {
        char buf[1024];
        ssize_t n = recv(sockfd, buf, sizeof(buf) - 1, 0);          // 不会阻塞
        if (n > 0)
        {
            buf[n] = 0;
            std::cout << "client say#" << buf << std::endl;     // 打印字符串信息
        }
        else if (n == 0)
        {
            LOG(LogLevel::DEBUG) << "client quit...";

            int ret = epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr); // 先注销
            if (ret > 0)
            {
                LOG(LogLevel::DEBUG) << "epoll_ctl remove sockfd:" << sockfd;
            }
            ::close(sockfd);                                            // 再关闭
        }
        else
        {
            LOG(LogLevel::ERROR) << "recv failure";

            int ret = epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr); // 先注销
            if (ret > 0)
            {
                LOG(LogLevel::DEBUG) << "epoll_ctl remove sockfd:" << sockfd;
            }
            ::close(sockfd);                                            // 再关闭
        }
    }

    void stop()
    {
        _isRunning = false;
    }

    ~EpollServer()
    {
        _listenSockfd->close();
        if (_epfd > 0)
        {
            ::close(_epfd);
        }
    }

private:

    std::unique_ptr<Socket> _listenSockfd;
    bool _isRunning;

    int _epfd;

    struct epoll_event _revs[EpollServer::size];
};
